﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace SectorPoint.Products.AdRotator
{
	[AspNetHostingPermissionAttribute( SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal )]
	[AspNetHostingPermissionAttribute( SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal )]
	[ToolboxData( "<{0}:AdRotatorWebPart runat=server />" )]
	[XmlRoot( Namespace = "AdRotatorWebPart" )]
	public class AdRotatorWebPart : System.Web.UI.WebControls.WebParts.WebPart, IWebEditable, IDesignTimeHtmlProvider
	{
		#region Constants
		const string MAIN_CATEGORY = @"Ad Rotator";
		const string CYCLE_CATEGORY = @"Cycle Settings";
		#endregion

		#region Public Properties

		/// <summary>
		/// Determins the rotation behavior.
		/// </summary>
		[WebPartStorage( Storage.Shared )]
		[Personalizable( PersonalizationScope.Shared )]
		[XmlElement( ElementName = "Rotation" )]
		public RotationOption Rotation { get; set; }

		/// <summary>
		/// Determines how JavaScript libraries will be served to visitors.
		/// </summary>
		[WebPartStorage( Storage.Shared )]
		[Personalizable( PersonalizationScope.Shared )]
		[XmlElement( ElementName = "JavaScriptDeliveryMethod" )]
		public ContentDeliveryOption JavaScriptDeliveryMethod { get; set; }

		/// <summary>
		/// URL of the list to pull images from
		/// </summary>
		[WebPartStorage( Storage.Shared )]
		[Personalizable( PersonalizationScope.Shared )]
		[XmlElement( ElementName = "ListUrl" )]
		public string ListUrl { get; set; }


        /// <summary>
        /// Css class to apply to webpart
        /// </summary>
        [WebPartStorage(Storage.Shared)]
        [Personalizable(PersonalizationScope.Shared)]
        [XmlElement(ElementName = "CustomCssClass")]
        public string CustomCssClass { get; set; }

		/// <summary>
		/// Milliseconds between slide transitions (0 to disable auto advance).
		/// </summary>
		[WebPartStorage( Storage.Shared )]
		[Personalizable( PersonalizationScope.Shared )]
		[XmlElement( ElementName = "RotationSpeed" )]
		public int RotationSpeed { get; set; }

		/// <summary>
		/// Transition effect to use.
		/// </summary>
		[WebPartStorage( Storage.Shared )]
		[Personalizable( PersonalizationScope.Shared )]
		[XmlElement( ElementName = "TransitionEffect" )]
		public string TransitionEffect { get; set; }

		/// <summary>
		/// True to enable "pause on hover"
		/// </summary>
		[WebPartStorage( Storage.Shared )]
		[Personalizable( PersonalizationScope.Shared )]
		[XmlElement( ElementName = "PauseOnHover" )]
		public bool PauseOnHover { get; set; }

		#endregion

		#region Constructor
		public AdRotatorWebPart()
		{
			this.ExportMode = WebPartExportMode.All;

			this.Rotation = RotationOption.Once;
			this.JavaScriptDeliveryMethod = ContentDeliveryOption.Internal;
			this.ListUrl = null;
			this.RotationSpeed = 4000;
			this.TransitionEffect = "fade";
			this.PauseOnHover = false;
		}
		#endregion

		#region Event Handlers
		protected override void CreateChildControls()
		{
			base.CreateChildControls();

			try
			{
				if (String.IsNullOrEmpty( this.ListUrl ))
				{
					if (this.WebPartManager.DisplayMode.AllowPageDesign)
					{
						CssRegistration.Register( "controls.css" );
						this.Controls.Add( new LiteralControl( String.Format( CultureInfo.CurrentCulture, "<p class=\"wp-content description\">This Web Part does not have a list configured. To configure this Web Part, <a href=\"#\" onclick=\"javascript:MSOTlPn_ShowToolPane2('Edit','{0}');\">open the tool pane</a>.</p>", new string[] { this.ID } ) ) );
					}

					this.ChildControlsCreated = true;
					return;
				}

				switch (this.Rotation)
				{
					case RotationOption.Multiple:
						this.AddJavaScript();
						this.CreateRotatingImages();
						break;

					case RotationOption.Once:
					default:
						this.Controls.Add( GetRandomImage() );
						break;
				}

				this.CssClass = "adRotator";		// TODO: Investigate why SharePoint is overriding this when it is in the constructor.
				if (!String.IsNullOrEmpty( this.CustomCssClass ))
					this.CssClass += " " + this.CustomCssClass;
					
			}
			catch (Exception ex)
			{
				this.Controls.Add( new LiteralControl( ex.ToString() ) );
			}

			this.ChildControlsCreated = true;
		}
		#endregion

		#region Methods
		/// <summary>
		/// Returns one random image from a SharePoint list.
		/// </summary>
		/// <returns>An image control, or a literal control if no images were found.</returns>
		protected virtual Control GetRandomImage()
		{
			Control returnValue = null;
			SPSite currentSite = SPContext.Current.Site;

			WebListUrl urls = new WebListUrl( this.ListUrl );

			using (SPWeb web = currentSite.AllWebs[urls.WebUrl])
			{
				SPList adList = web.Lists[urls.ListUrl];
				SPQuery adQuery = new SPQuery();
				adQuery.Query = "<OrderBy><FieldRef Name='Title' /></OrderBy>";
				SPListItemCollection adImages = adList.GetItems( adQuery );
				int imageCount = adImages.Count;

				if (imageCount > 0)
				{
					HtmlImage imgRandom = new HtmlImage();
					Random rnd = new Random( DateTime.Now.Millisecond );
					
					SPListItem randomItem = adImages[rnd.Next( 0, imageCount )];
					string altText = randomItem[SPBuiltInFieldId.Comments] as string;
					imgRandom.Alt = altText;
					if (String.IsNullOrEmpty( altText ))
						imgRandom.Attributes.Add( "alt", "" );
					
					imgRandom.Src = randomItem.File.ServerRelativeUrl;

					returnValue = imgRandom;

					// Check to see if the image has the URL column associated with it for us to build a link
					// TODO: Check the list to make sure the URL column is there, don't use try/catch
					
					if (adList.Fields.ContainsField("URL"))
					{
						object urlValue = randomItem[SPBuiltInFieldId.URL];
						if (urlValue != null)
						{
							SPFieldUrlValue imgUrl = new SPFieldUrlValue( (string)urlValue );
							HtmlAnchor imgLink = new HtmlAnchor();
							imgLink.HRef = imgUrl.Url;
							imgLink.Controls.Add( returnValue );

							returnValue = imgLink;
						}
					}
				}
				else
				{
					returnValue = new LiteralControl( "No images were found." );
				}
			}

			return returnValue;
		}

		/// <summary>
		/// Adds the JavaScript and CSS needed to drive the jQuery Cycle plugin
		/// </summary>
		[System.Security.Permissions.FileIOPermission( SecurityAction.LinkDemand )]
		protected virtual void AddJavaScript()
		{
			switch (this.JavaScriptDeliveryMethod)
			{
				case ContentDeliveryOption.Internal:
					ScriptLink.Register( this.Page, "jquery-1.4.2.min.js", false, false );
					ScriptLink.Register( this.Page, "jquery.cycle.all.min.js", false, false );
					break;

				case ContentDeliveryOption.None:
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQueryCycle", "~/_layouts/jquery.cycle.all.min.js" );
					break;

				case ContentDeliveryOption.Edgecast:
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQuery", "http://code.jquery.com/jquery-1.4.2.min.js" );
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQueryCycle", "~/_layouts/jquery.cycle.all.min.js" );
					break;

				case ContentDeliveryOption.Google:
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQuery", "http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" );
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQueryCycle", "~/_layouts/jquery.cycle.all.min.js" );
					break;

				case ContentDeliveryOption.Microsoft:
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQuery", "http://ajax.microsoft.com/ajax/jquery/jquery-1.4.2.min.js" );
					Page.ClientScript.RegisterClientScriptInclude( typeof( AdRotatorWebPart ), "jQueryCycle", "~/_layouts/jquery.cycle.all.min.js" );
					break;
			}

			string cssOptions = null;

			if (this.Width != Unit.Empty)
			{
				cssOptions = "width:" + this.Width.ToString( CultureInfo.CurrentCulture ) + ";";
			}

			if (this.Height != Unit.Empty)
			{
				cssOptions += "height:" + this.Height.ToString( CultureInfo.CurrentCulture ) + ";";
			}

			Page.ClientScript.RegisterClientScriptBlock( typeof( AdRotatorWebPart ), this.ClientID, @"
<script type=""text/javascript"">
//<![CDATA[
$(document).ready(function() {
    $('#" + this.ClientID + @"').cycle({
		fx: '" + this.TransitionEffect + @"',
		timeout: '" + this.RotationSpeed.ToString( CultureInfo.CurrentCulture ) + @"',
		pause: " + this.PauseOnHover.ToString().ToLower( CultureInfo.CurrentCulture ) + @"
	});
});
//]]>
</script>
<style type=""text/css"">
	#" + this.ClientID + @" img { border: 0;" + cssOptions + @" }
	.adRotator .notFirst { display: none; }
</style>
" );
		}

		/// <summary>
		/// Adds the images used for cycling images using the jQuery cycle plugin
		/// </summary>
		protected virtual void CreateRotatingImages()
		{
			SPSite currentSite = SPContext.Current.Site;

			WebListUrl urls = new WebListUrl( this.ListUrl );
			
			using (SPWeb web = currentSite.AllWebs[urls.WebUrl])
			{
				SPList adList = web.Lists[urls.ListUrl];
				bool hasLinks = false;
				SPQuery adQuery = new SPQuery();
				adQuery.Query = "<OrderBy><FieldRef Name='Title' /></OrderBy>";

				if (adList.Fields.ContainsField( "URL" ))
					hasLinks = true;
				
				bool isFirst = true;

				foreach (SPListItem listItem in adList.GetItems( adQuery ))
				{
					string imageSource = listItem.File.ServerRelativeUrl;
					string altText = listItem[SPBuiltInFieldId.Comments] as string;

					HtmlImage adImage = new HtmlImage();
					adImage.Alt = altText;
					adImage.Src = imageSource;
					
					if (hasLinks)
					{
						string imageLink = listItem[SPBuiltInFieldId.URL] as string;

						if (!String.IsNullOrEmpty( imageLink ))
						{
							SPFieldUrlValue imgUrl = new SPFieldUrlValue( (string)imageLink );
							HtmlAnchor imgLink = new HtmlAnchor();
							imgLink.HRef = imgUrl.Url;
							imgLink.Controls.Add( adImage );

							if (!isFirst)
								imgLink.Attributes.Add( "class", "notFirst" );
							else
								isFirst = false;

							this.Controls.Add( imgLink );

							continue;
						}
					}

					if (!isFirst)
						adImage.Attributes.Add( "class", "notFirst" );
					else
						isFirst = false;

					this.Controls.Add( adImage );
				}
			}
		}

		/// <summary>
		/// Parses the ListUrl property into two parts, a Web URL and a list name.
		/// </summary>
		struct WebListUrl
		{
			public string WebUrl { get; private set; }
			public string ListUrl { get; private set; }

			public WebListUrl(string fullUrl)
				: this()
			{
				int lastSlash = fullUrl.LastIndexOf( '/' ) + 1;
				this.WebUrl = fullUrl.Substring( 0, lastSlash );
				this.ListUrl = fullUrl.Substring( lastSlash );
			}
		}
		#endregion

		#region IWebEditable Members

		public override EditorPartCollection CreateEditorParts()
		{
			List<EditorPart> editorParts = new List<EditorPart>();
			editorParts.Add( new AdRotatorEditorPart() );

			return new EditorPartCollection( editorParts );
		}

		public override object WebBrowsableObject
		{
			get
			{
				return this;
			}
		}
		#endregion

		#region IDesignTimeHtmlProvider Members

		public string GetDesignTimeHtml()
		{
			return "<p>Ad Rotator Web Part</p>";
		}

		#endregion
	}
}
